package server

import (
	"fmt"
	core "gitee.com/dreamwood/ez"
	"strings"
	"time"
)

var routeHub *RouteHub

type RouteHub struct {
	//直接匹配的路由
	DirectRouter map[string]*EzHandler
	//正则匹配的路由
	RegxRouter map[string]*EzHandler
	//全局前置中间件
	Preparer map[string]*EzAction
	//全局后置中间件
	Finisher map[string]*EzAction
	//Handler前置操作,区别于前置操作，这时已经可以获取到route等信息了
	BeforeHandler map[string]*EzAction
	//Handler后置操作
	AfterHandler map[string]*EzAction
}

func NewRouteHub() *RouteHub {
	return &RouteHub{
		DirectRouter: make(map[string]*EzHandler),
		RegxRouter:   make(map[string]*EzHandler),
		//Coordination: make(map[string]*Coordination),
		Preparer:      make(map[string]*EzAction),
		Finisher:      make(map[string]*EzAction),
		BeforeHandler: make(map[string]*EzAction),
		AfterHandler:  make(map[string]*EzAction),
	}
}

func (this *RouteHub) AddDirectRoute(router *Router, action *EzAction) *EzHandler {
	hd := NewEzHandler(router, action)
	this.DirectRouter[router.Url] = hd
	return hd
}

func (this *RouteHub) AddRegxRoute(router *Router, action *EzAction) *EzHandler {
	//处理字符串转成合适的正则表达式
	pattern := core.ReplaceAll(router.Url, `:(\w+)\((.+?)\)`, "(?P<$1>$2)")
	hd := NewEzHandler(router, action)
	hd.Matcher = pattern
	this.RegxRouter[router.Url] = hd
	return hd
}

type Router struct {
	Url    string
	Group  string
	Prefix string
	Suffix string
}

func Do(router *Router, action EzAction) *EzHandler {
	url := router.Url
	if strings.Contains(url, ":") {
		return routeHub.AddRegxRoute(router, &action)
	} else {
		//直接路由注册
		return routeHub.AddDirectRoute(router, &action)
	}
}

func Dispatch(operation *Operation) {
	//有控制中心进行预处理的场景可以直接找到对应的处理程序
	//todo 控制中心进行处理完成之后把结果写在header头
	//todo 通过权限控制定位到matcher,同时确定是指向路由还是正则路由

	//没有注册中心的处理方式
	tRouter := operation.Timer.Add("Router")
	for url, hd := range routeHub.DirectRouter {
		if url == operation.Input.Url {
			tRouter.Done()
			PublicHandle(hd, operation)
			return
		}
	}
	for _, hd := range routeHub.RegxRouter {
		if core.Match(operation.Input.Url, hd.Matcher) {
			names := core.MatchAll(hd.Matcher, `<(.+?)>`)
			matches := core.MatchAll(operation.Input.Url, hd.Matcher)
			rebuild := make(map[string]string)
			for index, name := range names {
				rebuild[name[1]] = matches[0][index+1]
			}
			operation.Input.UrlData = rebuild
			tRouter.Done()
			PublicHandle(hd, operation)
			return
		}
	}
	tRouter.Done()
	urlNotFound(operation)
}

func PublicHandle(handler *EzHandler, operation *Operation) {
	defer func() {
		//执行过程中的所有数据都写入了Writer
		_, e := operation.Output.Response.Write(operation.Output.Writer.Bytes())
		if e != nil {
			println("Can not write to response", e.Error())
		}
	}()

	//在此之前handler是空的
	operation.Handler = handler
	tBefore := operation.Timer.Add("BeforeHandler")
	//添加一些公共的前置操作
	for name, action := range routeHub.BeforeHandler {
		tPrepareItem := operation.Timer.Add(name)
		(*action)(operation)
		tPrepareItem.Done()
		if operation.IsStop {
			return
		}
	}
	tBefore.Done()

	tMainHandler := operation.Timer.Add("MainHandler")
	//执行处理
	handler.Run(operation)
	tMainHandler.Done()
	//加入日志系统
	core.Debug(fmt.Sprintf("%s [%s] |%s| %s Url:%s, Match:%s",
		//time.Now().Format("2006-01-02 15:04:05.000"),
		time.Now().Format("15:04:05.000"),
		operation.Input.Request.RemoteAddr,
		operation.Input.Request.Method,
		tMainHandler.CostString,
		operation.Input.Url,
		handler.Matcher))

	tAfter := operation.Timer.Add("GlobalFinisher")
	//添加一些公共的后续操作

	for name, action := range routeHub.AfterHandler {
		tFinisherItem := operation.Timer.Add(name)
		(*action)(operation)
		tFinisherItem.Done()
		if operation.IsStop {
			return
		}
	}
	tAfter.Done()

}

func urlNotFound(operation *Operation) {
	operation.Output.Response.WriteHeader(404)
	operation.Output.Response.Write([]byte("URL Not Match"))
}

func init() {
	routeHub = NewRouteHub()
}

func AddPreparer(name string, action EzAction) {
	routeHub.Preparer[name] = &action
}
func AddFinisher(name string, action EzAction) {
	routeHub.Finisher[name] = &action
}

func AddBeforeHandler(name string, action EzAction) {
	routeHub.BeforeHandler[name] = &action
}
func AddAfterHandler(name string, action EzAction) {
	routeHub.AfterHandler[name] = &action
}

func GetRouteHub() *RouteHub {
	return routeHub
}
